package sli.sli2.pcp.service;

import java.util.Date;
import java.util.HashMap;
import java.util.Vector;

import sli.sli2.domain.AccessPermission;
import sli.sli2.domain.User;
import sli.sli2.domain.Zone;

/**
 *
 * @author Roman Khassraf <roman at khassraf.at>
 */
public class AuthorizationService {
    
	private static final Vector<AccessPermission> permissions = new Vector<AccessPermission>(); 
	private static final HashMap<Integer, Zone> zones = new HashMap<Integer, Zone>();
	private static final HashMap<String, User> users = new HashMap<String, User>();
    private static final HashMap<User, Integer> authAttempts = new HashMap<User, Integer>();

	private LoggingService loggingService;
	
	public AuthorizationService(LoggingService loggingService) {
		
		this.loggingService = loggingService;
		
		Date d1 = new Date(); // now 
		Date d2 = new Date(System.currentTimeMillis() + 1000*60*60); // in one hour
		
		AccessPermission ap1 = new AccessPermission(1, 1000, d1, d2); // should be valid for zone 1000 for one hour
		
		Date d3 = new Date(System.currentTimeMillis() - 1000*60*60); // one hour ago
		Date d4 = new Date(System.currentTimeMillis() - 1000); // one second ago
		
		AccessPermission ap2 = new AccessPermission(2, 1000, d3, d4); // should have expired a second ago
		AccessPermission ap3 = new AccessPermission(3, 2000, d1, d2); // valid for a different zone
		
		permissions.add(ap1);
		permissions.add(ap2);
		permissions.add(ap3);
		
		
		Zone z1 = new Zone(1000, 11, false, false);
		Zone z2 = new Zone(2000, 22, true, true);
		Zone z3 = new Zone(3000, 33, true, false);
		
		zones.put(1000, z1);
		zones.put(2000, z2);
		zones.put(3000, z3);
		
		String key1 = "AA11";
		String key2 = "BB22";
		String key3 = "CC33";
		
		User u1 = new User(1, "Max", "Mustermann", 1111, "AAAAAAAA");
		User u2 = new User(2, "Stefan", "Müller", 2222, "BBBBBBBB");
		User u3 = new User(3, "Roman", "Zischka", 3333, "CCCCCCCC");
		
		users.put(key1, u1);
		users.put(key2, u2);
		users.put(key3, u3);
		
		
	}
	
    public String checkAuth(String authHash, int zoneId, int terminalId) {
        
       	return checkPermission(authHash, zoneId, terminalId, null, null);
    }
    
    public String checkAuth(String authHash, int zoneId, int terminalId, Integer pin) {
        
       	return checkPermission(authHash, zoneId, terminalId, pin, null);
    }
    
    public String checkAuth(String authHash, int zoneId, int terminalId, String biometricHash) {
        return checkPermission(authHash, zoneId, terminalId, null, biometricHash);
    }

    public String checkAuth(String authHash, int zoneId, int terminalId, Integer pin, String biometricHash) {
    	return checkPermission(authHash, zoneId, terminalId, pin, biometricHash);
    }
    
    private String checkPermission(String authHash, int zoneId, int terminalId, Integer pin, String biometricHash) {
        
    	Zone z = zones.get(zoneId);
    	
    	if (!users.containsKey(authHash)) {
    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User " + authHash + " does not exist");
    		return "error user does not exist";
    	}
    	if (!zones.containsKey(zoneId)) {
    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: Zone " + zoneId + " does not exist");
    		return "error zone does not exist";
    	}
    	
    	User u = users.get(authHash);
        int attemptCount = 0; 
        
        if (authAttempts.containsKey(u)) {
            attemptCount = authAttempts.get(u);
            if (attemptCount >= 3 ) {
                loggingService.addLogEntryAndNotify(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " more than 3 unsuccessful attempts", "security staff", "alert");
                authAttempts.put(u, ++attemptCount);
                return "error access denied due excessive login attempts";
            }
        }
    	for (AccessPermission ap : permissions) {
    		
    		if (ap.getUserId() == u.getUserId() && ap.getZoneId() == zoneId) {
    			
    	    	Date now = new Date();
    	    	if (now.compareTo(ap.getStartTime()) < 0 || now.compareTo(ap.getEndTime()) > 0) {
    	    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " invalid access time");
                    authAttempts.put(u, ++attemptCount);
    	    		return "error zone access denied due to time constraints";
    	    	}
    	    	
    	    	if (z.requiresPin() && pin == null) {
    	    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " missing PIN");
                    authAttempts.put(u, ++attemptCount);
    	    		return "error zone requires pin";
    	    	}
    	    	if (z.requiresEye() && biometricHash == null) {
    	    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " missing biometric information");
                    authAttempts.put(u, ++attemptCount);
    	    		return "error zone requires biometric authentication";
    	    	}
    	    	
    	    	if (z.requiresPin() && !pin.equals(u.getPin())) {
    	    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " incorrect PIN");
                    authAttempts.put(u, ++attemptCount);
    	    		return "error zone access denied due to incorrect pin";
    	    	}
    	    	if (z.requiresEye() && !biometricHash.equals(u.getBiometricHash()))  {
    	    		loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " incorrect biometric information");
                    authAttempts.put(u, ++attemptCount);
    	    		return "error zone access denied due to biometric mismatch";
    	    	}
    	    	
    	    	loggingService.addLogEntry(z.getObjectId(), terminalId, "Successful Login: User: " + u.getUserId());
                authAttempts.put(u, new Integer(0));
    	    	return "ok";
    		}
    		
    	}
    	
    	loggingService.addLogEntry(z.getObjectId(), terminalId, "Attempted Login: User: " + u.getUserId() + " not authorized");
        authAttempts.put(u, ++attemptCount);
    	return "zone access denied, user not authorized for zone";
    }
    
}
